package com.gitee.wsl.collections.map.api

import com.gitee.wsl.collections.iterator.MutableMapFunIterator
import com.gitee.wsl.collections.set.api.MultiSet


/**
 * Defines a map that holds a collection of values against each key.
 *
 *
 * A `MultiValuedMap` is a Map with slightly different semantics:
 *
 *
 *  * Putting a value into the map will add the value to a [Collection] at that key.
 *  * Getting a value will return a [Collection], holding all the values put to that key.
 *
 *
 *
 * For example:
 *
 * <pre>`MultiValuedMap<Integer, String> map = new ArrayListValuedHashMap<>();
 * map.put(1, "A");
 * map.put(1, "B");
 * map.put(1, "C");
 * Collection<String> coll = map.get(1);
`</pre> *
 *
 *
 * `coll` will be a collection containing "A", "B", "C".
 *
 *
 * @param <K> the type of the keys in this map
 * @param <V> the type of the values in this map
 * @since 4.1
</V></K> */
interface MultiValuedMap<K, V> {
    // Query operations
    /**
     * Returns a view of this multivalued map as a `Map` from each distinct
     * key to the non-empty collection of that key's associated values.
     *
     *
     * Note that `this.asMap().get(k)` is equivalent to `this.get(k)`
     * only when `k` is a key contained in the multivalued map; otherwise it
     * returns `null` as opposed to an empty collection.
     *
     *
     *
     * Changes to the returned map or the collections that serve as its values
     * will update the underlying multivalued map, and vice versa. The map does
     * not support `put` or `putAll`, nor do its entries support
     * [setValue][java.util.Map.Entry.setValue].
     *
     *
     * @return a map view of the mappings in this multivalued map
     */
    fun asMap(): Map<K, Collection<V>>

    /**
     * Removes all of the mappings from this map (optional operation).
     *
     *
     * The map will be empty after this call returns.
     *
     *
     * @throws UnsupportedOperationException if the map is unmodifiable
     */
    fun clear()

    /**
     * Returns `true` if this map contains a mapping for the specified
     * key. More formally, returns `true` if and only if this map contains
     * a mapping for a key `k` such that `(key==null ? k==null : key.equals(k))`.
     * (There can be at most one such mapping.)
     *
     * @param key  key whose presence in this map is to be tested
     * @return true if this map contains a mapping for the specified key
     * @throws NullPointerException if the specified key is null and this map
     * does not permit null keys (optional)
     */
    fun containsKey(key: K): Boolean

    /**
     * Checks whether the map contains a mapping for the specified key and value.
     *
     * @param key  the key to search for
     * @param value  the value to search for
     * @return true if the map contains the value
     */
    fun containsMapping(key: K, value: V): Boolean

    /**
     * Checks whether the map contains at least one mapping for the specified value.
     *
     * @param value  the value to search for
     * @return true if the map contains the value
     * @throws NullPointerException if the value is null and null values are not supported
     * by the used collection types (optional)
     */
    fun containsValue(value: V): Boolean

    /**
     * Returns a [Collection] view of the mappings contained in this multivalued map.
     *
     *
     * The collection is backed by the map, so changes to the map are reflected
     * in the collection, and vice-versa.
     *
     *
     * @return a set view of the mappings contained in this map
     */
    fun entries(): Collection<Map.Entry<K, V>>

    // Modification operations
    /**
     * Returns a view collection of the values associated with the specified key.
     *
     *
     * This method will return an **empty** collection if [.containsKey]
     * returns `false`. Changes to the returned collection will update the underlying
     * `MultiValuedMap` and vice-versa.
     *
     *
     * @param key  the key to retrieve
     * @return the `Collection` of values, implementations should
     * return an empty collection for no mapping
     * @throws NullPointerException if the key is null and null keys are invalid (optional)
     */
    fun get(key: K): Collection<V>

    /**
     * Returns `true` if this map contains no key-value mappings.
     *
     * @return `true` if this map contains no key-value mappings
     */
    val isEmpty: Boolean

    /**
     * Returns a [MultiSet] view of the keys contained in this multivalued map.
     *
     *
     * The [MultiSet.getCount] method of the returned multiset will give
     * the same result a calling `get(Object).size()` for the same key.
     *
     *
     *
     * This multiset is backed by the map, so any changes in the map are reflected in
     * the multiset.
     *
     *
     * @return a multiset view of the keys contained in this map
     */
    val keys: MultiSet<K>

    /**
     * Returns a [Set] view of the keys contained in this multivalued map.
     *
     *
     * The set is backed by the map, so changes to the map are reflected
     * in the set, and vice-versa.
     *
     *
     *
     * If the map is modified while an iteration over the set is in
     * progress (except through the iterator's own `remove` operation),
     * the result of the iteration is undefined. The set supports element
     * removal, which removes the corresponding mapping from the map, via the
     * `Iterator.remove`, `Set.remove`, `removeAll`,
     * `retainAll`, and `clear` operations. It does not support
     * the `add` or `addAll` operations.
     *
     *
     * @return a set view of the keys contained in this map
     */
    fun keySet(): Set<K>

    /**
     * Obtains a `MapIterator` over this multivalued map.
     *
     *
     * A map iterator is an efficient way of iterating over maps. There is no
     * need to access the entries collection or use `Map.Entry` objects.
     *
     *
     * @return a map iterator
     */
    fun mapIterator(): MutableMapFunIterator<K, V>

    /**
     * Adds a key-value mapping to this multivalued map.
     *
     *
     * Unlike a normal `Map` the previous value is not replaced.
     * Instead, the new value is added to the collection stored against the key.
     * Depending on the collection type used, duplicate key-value mappings may
     * be allowed.
     *
     *
     *
     * The method will return `true` if the size of the multivalued map
     * has been increased because of this operation.
     *
     *
     * @param key  the key to store against
     * @param value  the value to add to the collection at the key
     * @return true if the map changed as a result of this put operation, or false
     * if the map already contained the key-value mapping and the collection
     * type does not allow duplicate values, e.g. when using a Set
     * @throws UnsupportedOperationException if the put operation is not supported by
     * this multivalued map, e.g. if it is unmodifiable
     * @throws NullPointerException if the key or value is null and null is invalid (optional)
     * @throws IllegalArgumentException if some aspect of the specified key or value prevents
     * it from being stored in this multivalued map
     */
    fun put(key: K, value: V): Boolean

    /**
     * Adds a mapping to the specified key for all values contained in the given Iterable.
     *
     * @param key  the key to store against
     * @param values  the values to add to the collection at the key, may not be null
     * @return true if the map changed as a result of this operation
     * @throws NullPointerException if the specified iterable is null, or if this map
     * does not permit null keys or values, and the specified key or values contain
     * null (optional)
     */
    fun putAll(key: K, values: Iterable<V>): Boolean

    /**
     * Copies all mappings from the specified map to this multivalued map
     * (optional operation).
     *
     *
     * The effect of this call is equivalent to that of calling
     * [put(k, v)][.put] on this map once for each mapping
     * from key `k` to value `v` in the specified map.
     *
     *
     *
     * The behavior of this operation is undefined if the specified map is modified
     * while the operation is in progress.
     *
     *
     * @param map  mappings to be stored in this map, may not be null
     * @return true if the map changed as a result of this operation
     * @throws UnsupportedOperationException if the `putAll` operation is
     * not supported by this map
     * @throws NullPointerException if the specified map is null, or if this map
     * does not permit null keys or values, and the specified map
     * contains null keys or values (optional)
     * @throws IllegalArgumentException if some property of a key or value in
     * the specified map prevents it from being stored in this map
     */
    fun putAll(map: Map<K, V>): Boolean

    /**
     * Copies all mappings from the specified map to this multivalued map
     * (optional operation).
     *
     *
     * The effect of this call is equivalent to that of calling
     * [put(k, v)][.put] on this map once for each
     * mapping from key `k` to value `v` in the specified map.
     *
     *
     *
     * The behavior of this operation is undefined if the specified map is modified
     * while the operation is in progress.
     *
     *
     * @param map  mappings to be stored in this map, may not be null
     * @return true if the map changed as a result of this operation
     * @throws UnsupportedOperationException if the `putAll` operation is
     * not supported by this map
     * @throws NullPointerException if the specified map is null, or if this map
     * does not permit null keys or values, and the specified map
     * contains null keys or values (optional)
     * @throws IllegalArgumentException if some property of a key or value in
     * the specified map prevents it from being stored in this map
     */
    fun putAll(map: MultiValuedMap<K, V>): Boolean

    /**
     * Removes all values associated with the specified key.
     *
     *
     * The returned collection *may* be modifiable, but updates will not be propagated
     * to this multivalued map. In case no mapping was stored for the specified
     * key, an empty, unmodifiable collection will be returned.
     *
     *
     * @param key  the key to remove values from
     * @return the values that were removed
     * @throws UnsupportedOperationException if the map is unmodifiable
     * @throws NullPointerException if the key is null and null keys are invalid (optional)
     */
    fun remove(key: K): Collection<V>

    /**
     * Removes a key-value mapping from the map.
     *
     *
     * The item is removed from the collection mapped to the specified key.
     * Other values attached to that key are unaffected.
     *
     *
     *
     * If the last value for a key is removed, implementations typically return
     * an empty collection from a subsequent `get(Object)`.
     *
     *
     * @param key  the key to remove from
     * @param item  the item to remove
     * @return true if the mapping was removed, false otherwise
     * @throws UnsupportedOperationException if the map is unmodifiable
     * @throws NullPointerException if the key or value is null and null is invalid (optional)
     */
    fun removeMapping(key: K, item: V): Boolean

    /**
     * Gets the total size of the map.
     *
     *
     * Implementations would return the total size of the map which is the count
     * of the values from all keys.
     *
     *
     * @return the total size of the map
     */
    val size: Int

    /**
     * Gets a [Collection] view of all values contained in this multivalued map.
     *
     *
     * Implementations typically return a collection containing the combination
     * of values from all keys.
     *
     *
     * @return a collection view of the values contained in this multivalued map
     */
    fun values(): Collection<V>
}
